import sys, re, urllib2, os, time, datetime
import threading, Queue
import showdb
import feedparser
from sqlobject import AND

try:
	from ConfigParser import SafeConfigParser as ConfigParser
except ImportError:
	from ConfigParser import ConfigParser

try:
	import magic
	m = magic.open(magic.MAGIC_MIME_TYPE)
	m.load()
	def is_torrent(f):
		return m.file(f) == 'application/x-bittorrent'
except:
	def is_torrent(f):
		return True

import os
CONFIG_FILE = os.path.join(os.environ['HOME'], '.wjs.ini')

def create_feed(url, redownload = False):
	d = feedparser.parse(url)
	return showdb.Feed(url = url, name = d.feed.title, redownload = redownload)

def get_feed_entries(feed):
	etag, modified = feed.eTag, feed.lastModified
	if modified:
		modified = modified.timetuple()
	d = feedparser.parse(feed.url, etag = etag, modified = modified)
	if d.status == 410:
		print(u"FEED  %s REMOVED\n" % feed.name)
		showdb.Feed.delete(feed)
		return None
	elif d.status == 301:
		print(u"FEED  %s UPDATED, new url: %s\n" % (feed.name, d.href))
		feed.url = d.href
	feed.eTag = d.get('etag')
	modified = d.get('modified')
	if modified:
		feed.lastModified = datetime.datetime.fromtimestamp(time.mktime(modified))
	return d.get('entries')

def search_episode_number(name):
	for pattern in (ur'(\d+)x(\d+)', ur's(\d+)e(\d+)'):
		m = re.search(pattern, name, re.I)
		if m:
			return int(m.group(1)), int(m.group(2))
	return None, None

def search_episode_proper(name):
	m = re.search('proper', name, re.I)
	if m:
		return True

def blacklisted(name):
	for bl in showdb.BlacklistRegExp.select():
		if re.search(bl.pattern, name, re.I):
			return True

def skip(name):
	if blacklisted(name):
		return True
	previous = showdb.Match.select(showdb.Match.q.value == name)
	if previous.count() > 0:
		return True
	return False

def get_episode_match_info(name, match):
	season_number = None
	episode_number = None
	proper = False
	try:
		season_number = int(match.group('S'))
	except IndexError:
		pass
	try:
		episode_number = int(match.group('E'))
	except IndexError:
		pass
	try:
		proper = bool(match.group(['P']))
	except IndexError:
		pass
	if not season_number and not episode_number:
		season_number, episode_number = search_episode_number(name)
	if not proper:
		proper = search_episode_proper(name)
	return season_number, episode_number, proper

def get_download_url(entry):
	for enc in entry.enclosures:
		if enc.type == u'application/x-bittorrent':
			return enc.href

def get_entry_filename(entry, dest = None):
	name = os.path.basename(entry.title)
	if not name.endswith('.torrent'):
		name += '.torrent'
	if dest:
		name = os.path.join(dest, name)
	return name


class Downloader:
	def __init__(self):
		queue = Queue.Queue()
		def worker(handler, queue):
			while True:
				url, filename, attempt = queue.get()
				try:
					if attempt < MAX_ATTEMPTS:
						handler(url, filename)
					else:
						handler.failed(url)
				except DownloadError:
					queue.put(url, filename, attempt + 1)
				finally:
					queue.task_done()
		t = threading.Thread(target = worker, args = (self, queue,))
		t.daemon = True
		t.start()
		self.queue = queue
	def __call__(self, url, filename):
		src = urllib2.urlopen(url)
		dst = open(filename, 'wb')
		dst.write(src.read())
		src.close()
		dst.close()
		time.sleep(60)
	def add(self, url, filename):
		self.queue.put((url, filename, 0))
	def join(self):
		self.queue.join()

downloader = Downloader()

def handle_entry(entry, redownload = False, dest = None):
	name = entry.title
	for regexp in showdb.RegExp.select():
		match = re.match(regexp.pattern, name, re.I)
		if match and not skip(name):
			season_number, episode_number, proper = get_episode_match_info(name, match)
			if regexp.show:
				previous = showdb.Match.select(AND(AND( \
					showdb.Match.q.show == regexp.show, \
					showdb.Match.q.season_number == season_number), \
					showdb.Match.q.episode_number == episode_number))
				has_previous = previous.count() > 0
				if (not redownload or not proper) and has_previous:
					print "%s - %dx%d - already downloaded" % (regexp.show.name, season_number, episode_number)
				else:
					print "%s - %dx%d%s - downloading (%s)" % (regexp.show.name, season_number, episode_number,
					' (PROPER)' if proper else '', \
					previous[0].value if has_previous else '')
				showdb.Match(value = name, show = regexp.show, \
					season_number = season_number, \
					episode_number = episode_number)
				downloader.add(get_download_url(entry), \
					get_entry_filename(entry, dest))
			else:
				filename = get_entry_filename(entry)
				previous = showdb.Download.select(showdb.Download.q.name == filename)
				has_previous = previous.count() > 0
				if not has_previous:
					showdb.Download(name = filename)
					downloader.add(get_download_url(entry), \
						get_entry_filename(entry, dest))

def main(conf = CONFIG_FILE):
	config = ConfigParser()
	config.read(conf)
	showdb.init(config.get("dsn", "shows"))
	dest = config.get("path", "shows")
	for feed in showdb.Feed.select():
		entries = get_feed_entries(feed)
		if not entries:
			print "%s - no new entries, skipped" % feed.name
		else:
			print "%s - %d entries" % (feed.name, len(entries))
		for entry in entries:
			handle_entry(entry, feed.redownload, dest)

if __name__ == '__main__':
	showdb.init()
	main()
	downloader.join()
